namespace How_toEval;

using static AbstractSyntaxTree;

public class Parser
{
    private List<Token> Tokens { get; set; }

    public Parser(List<Token> tokens, int pos = 0)
    {
        Tokens = tokens;
        Pos = pos;
    }

    private int Pos { get; set; }

    private Token? Read()
    {
        if (Pos < Tokens.Count)
        {
            return Tokens[Pos];
        }
        else
        {
            return null;
        }
    }

    private Token? Peek()
    {
        if (Pos < Tokens.Count - 1)
        {
            return Tokens[Pos + 1];
        }
        else
        {
            return null;
        }
    }

    private Token? Consume()
    {
        if (Pos < Tokens.Count)
        {
            var token = Tokens[Pos];
            Pos += 1;
            return token;
        }
        else return null;
    }

    public AbstractSyntaxTree Expression()
    {
        return _Expression(0);
    }

    /// <summary>
    /// 被括号包围的表达式
    /// </summary>
    public AbstractSyntaxTree SurroundedExpression()
    {
        var exp = Expression();
        if (Read() == null) throw new Exception("Unexpected EOF");
        if (Read()!.Type != TokenType.RightParentheses)
        {
            throw new Exception("Expected )");
        }
        Consume(); // 消耗右括号
        return exp;
    }


    /// <summary>
    /// 解释单元 : Expression EOF
    /// </summary> 
    public AbstractSyntaxTree Unit()
    {
        var exp = Expression();
        if (Read() is Token t)
        {
            throw new Exception($"Unexpected token {t}");
        }
        return exp;
    }


    /// <summary>
    /// 带有Minimun Binding Power的Expression Parser
    /// </summary>
    public AbstractSyntaxTree _Expression(int min_bp = 0)
    {
        AbstractSyntaxTree lhs = Consume() switch
        {
            (TokenType.Integer, var i) => new Integer(int.Parse(i)),
            (TokenType.Float, var f) => new Float(double.Parse(f)),
            (TokenType.Symbol, var s) => new Var(s),
            (TokenType.LeftParentheses, _) => SurroundedExpression(),
            (TokenType.BinaryOperator, var op) => PrefixedExpression(op),
            (TokenType.Begin, _) => GroupedExpression(),
            (TokenType.Lambda, _) => FunctionDefine(),
            var t => throw new Exception($"Unexpected token {t}")
        }; // 所有作为表达式开头的情况


        while (true)
        {
            switch (Read())
            {
                case Token(TokenType.BinaryOperator
                            or TokenType.Define
                            or TokenType.Colon, var o): // 中缀操作符
                    {
                        var ((lbp, rbp), handler) = InfixBindingPower(o);
                        if (lbp < min_bp)
                        {
                            return lhs;
                        }
                        Consume();

                        var rhs = _Expression(rbp);
                        lhs = handler(lhs, rhs, o);
                        break;
                    } // 这里的大括号只是用来限制lbp, rbp的作用域

                case Token(TokenType.LeftParentheses, var o): // 后缀操作符
                    {
                        var lbp = PostfixBindingPower(o);
                        if (lbp < min_bp)
                        {
                            return lhs;
                        }
                        Consume();

                        lhs = PostfixedExpression(lhs, o);
                        break;
                    }

                default:
                    return lhs; // 如果读到的符号不再能作为本层表达式的一部分，则交给上层调用者处理
            } // 所有作为表达式中间和末尾的情况
        }
    }


    private AbstractSyntaxTree GroupedExpression()
    {
        var exps = new List<AbstractSyntaxTree>();
        while (true)
        {
            switch (Read())
            {
                case (TokenType.End, _):
                    Consume();
                    return new Group(exps);
                case (TokenType.Comma, _):
                    Consume();
                    break;
                default:
                    exps.Add(Expression());
                    HereShouldBe(new HashSet<TokenType>()
                    {
                        TokenType.End,
                        TokenType.Comma
                    });
                    break;
            }
        }
    }

    public AbstractSyntaxTree PostfixedExpression(AbstractSyntaxTree lhs, string op)
    {
        return op switch
        {
            "(" => FunctionCall(lhs),
            _ => throw new Exception("Expected (")
        };
    }

    public Function FunctionDefine()
    {
        if (Read() == null || Read()!.Type != TokenType.LeftParentheses) { throw new Exception("Expected ("); }
        Consume();
        List<(string, string)> @params = new List<(string, string)>();
        while (true)
        {
            switch (Read())
            {
                case (TokenType.Symbol, var param):
                    {
                        Consume();
                        if (Read() != null && Read()!.Type == TokenType.Colon) // 有参数注解
                        {
                            Consume();
                            HereShouldBe(new HashSet<TokenType>()
                            {
                                TokenType.Symbol
                            });
                            @params.Add((param, Consume()!.Content));
                        }
                        else // 无参数注解
                        {
                            @params.Add((param, ""));
                        }
                        HereShouldBe(new HashSet<TokenType>()
                        {
                            TokenType.Comma,
                            TokenType.RightParentheses
                        });
                        break;
                    }
                case (TokenType.Comma, _):
                    Consume();
                    break;
                case (TokenType.RightParentheses, _):
                    Consume();
                    if (Read() == null || Read()!.Type != TokenType.Arrow) return new Function(Expression(), @params, "");
                    Consume();
                    HereShouldBe(new HashSet<TokenType>()
                        {
                            TokenType.Symbol
                        });
                    var tp = Consume()!.Content;
                    return new Function(Expression(), @params, tp);
                default:
                    throw new Exception($"Unexpected Token {Read()}");
            }
        }
    }

    public Call FunctionCall(AbstractSyntaxTree lhs)
    {
        var @params = new List<AbstractSyntaxTree>();
        while (true)
        {
            switch (Read())
            {
                case (TokenType.Comma, _):
                    Consume();
                    break;
                case (TokenType.RightParentheses, _):
                    Consume();
                    return new Call(lhs, @params);
                default:
                    {
                        @params.Add(Expression());
                        HereShouldBe(new HashSet<TokenType>()
                        {
                            TokenType.Comma,
                            TokenType.RightParentheses
                        });
                        break;
                    }
            }
        }
    }

    private void HereShouldBe(HashSet<TokenType> tktypes)
    {
        var r = Read();
        if (r == null) throw new Exception($"Expected a {string.Join(" or ", tktypes)})");
        if (!tktypes.Contains(r.Type)) throw new Exception($"Expected a {string.Join(" or ", tktypes)})");
    }

    public AbstractSyntaxTree PrefixedExpression(string op)
    {
        var rhs = _Expression(PrefixBindingPower(op));
        return op switch
        {
            "+" => new BinaryOperateNode(new Integer(0), rhs, "+"),
            "-" => new BinaryOperateNode(new Integer(0), rhs, "-"),
            var t => throw new Exception($"Expected an unary operator, but got a {t}")
        };
    }

    public delegate AbstractSyntaxTree InfixHandler(AbstractSyntaxTree lhs,
                                                     AbstractSyntaxTree rhs,
                                                     string op);

    public ((int, int), InfixHandler) InfixBindingPower(string op)
    {
        return op switch
        {
            "+" or "-" => ((11, 12), (l, r, o) => new BinaryOperateNode(l, r, o)),
            "*" or "/" => ((21, 22), (l, r, o) => new BinaryOperateNode(l, r, o)),
            ":" => ((101, 102), AnnotationHandler),
            ":=" => ((2, 1), DefineHandler),
            _ => throw new Exception("Expected an operator")
        };
    }

    public AbstractSyntaxTree AnnotationHandler(AbstractSyntaxTree lhs, AbstractSyntaxTree rhs, string _)
    {
        return rhs switch
        {
            Var v => new AnnotatedExpression(lhs, v.Name),
            _ => throw new Exception("The expression after the colon cannot be used as annotation")
        };
    }

    public AbstractSyntaxTree DefineHandler(AbstractSyntaxTree lhs, AbstractSyntaxTree rhs, string _)
    {
        return lhs switch
        {
            Var v => new Define("", v.Name, rhs),
            AnnotatedExpression a when a.Expression is Var v => new Define(a.Annotation, v.Name, rhs),
            _ => throw new Exception("The expression before the assignment operator is not assignable")
        };
    }

    public int PrefixBindingPower(string op)
    {
        return op switch
        {
            "+" or "-" => 32,
            _ => throw new Exception("Expected an unary operator")
        };
    }

    public int PostfixBindingPower(string op)
    {
        return op switch
        {
            "(" => 42,
            _ => throw new Exception("Expected an postfix operator")
        };
    }
}

